The last day is definitely the most fun. We get to play with metaprogramming with one bigger question, of which more shortly. My reflections on Ruby were it was cool to catch up with the language. I’d sort of forgotten quite how nifty it was to play with. For my money its more Perlish than the other popular OO scripting language Python, which is a very good thing in my book. Perhaps, I’ll make something with Ruby rather than Perl next time I’m hacking a project, who knows?
The next language is Io. Unlike Ruby I have not even touched Io before. So I anticipate a bit more of a challenge. I will probably make a start some time next week. To give my brain a bit of a rest. And to recover my poor social life a bit ;-)
acts_as_csv.rb
Modify the CSV application to support an each method to return a CsvRow object. Use method_missing on that CsvRow to return the value for the column for a given heading.
I shall have a trawl around to see how other folks did this. I’m not so happy with the nested loop in the read method. I just extended Hash to make my CsvRow object. It does most of what I want, I got a bit stuck on the method_missing syntax but Code friendly hashes in Ruby with method_missing on agile advisor helped me see things were way less difficult than I’d imagined.
My data file was:
one, two, three
chips, beans, mushrooms
cider, lager, vodka
techno, gabba, house
My code was:
#!/usr/bin/ruby
module ActsAsCsv
# A CsvRow is basically a Hash keyed on the column name innit?
class CsvRow < Hash
def method_missing(name)
return self[name.to_s]
end
end
def self.included base
base.extend ClassMethods
end
module ClassMethods
def acts_as_csv
include InstanceMethods
end
end
module InstanceMethods
def read
@csv_contents = []
filename = self.class.to_s.downcase + '.csv'
f = File.new(filename)
@headers = f.gets.chomp.split(', ')
rowcount = 0
f.each do |row|
cellcount = 0
row.chomp.split(', ').each do |cell|
if @csv_contents[rowcount] == nil
@csv_contents[rowcount] = CsvRow.new
end
@csv_contents[rowcount][@headers[cellcount]] = cell.chomp
cellcount = cellcount + 1
end
rowcount = rowcount + 1
end
end
attr_accessor :headers, :csv_contents, :csv_rows
def initialize
read
end
def each
@csv_contents.each do |row|
yield row
end
end
end
end
class RubyCsv
include ActsAsCsv
acts_as_csv
end
r = RubyCsv.new
r.each {|row| puts row.one}
Running it produces:
$ ./acts_as_csv.rb
chips
cider
techno
$